/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "sv_local.h"


/*
 ==================
 SV_UpdateMaxClients
 ==================
*/
static void SV_UpdateMaxClients (void){

	client_t	*client, *clients;
	int			numClients, maxClients;
	int			i;

	numClients = 0;
	maxClients = sv_maxClients->integerValue;

	// Count connected clients
	for (i = 0; i < maxClients; i++){
		if (svs.clients[i].state < CS_CONNECTED)
			continue;

		numClients++;
	}

	// Update sv_maxClients variable
	sv_maxClients = CVar_Get("sv_maxClients", "32", 0, NULL);

	// Guarantee slots for all connected clients
	CVar_SetInteger(sv_numClients, numClients);

	if (sv_maxClients->integerValue < numClients)
		CVar_SetInteger(sv_maxClients, numClients);

	if (sv_maxClients->integerValue < 16)
		CVar_SetInteger(sv_maxClients, 16);
	else if (sv_maxClients->integerValue > MAX_CLIENTS)
		CVar_SetInteger(sv_maxClients, MAX_CLIENTS);

	// Check if it changed at all
	if (sv_maxClients->integerValue == maxClients)
		return;

	// Copy all connected clients to a temporary buffer
	clients = client = (client_t *)Mem_Alloc(numClients * sizeof(client_t), TAG_TEMP);

	for (i = 0; i < maxClients; i++){
		if (svs.clients[i].state < CS_CONNECTED)
			continue;

		memcpy(client, &svs.clients[i], sizeof(client_t));

		client->clientNum = client - clients;

		client++;
	}

	// Reallocate the client slots
	Mem_Free(svs.clients);

	svs.clients = (client_t *)Mem_ClearedAlloc(sv_maxClients->integerValue * sizeof(client_t), TAG_COMMON);

	memcpy(svs.clients, clients, numClients * sizeof(client_t));

	// Free the temporary buffer
	Mem_Free(clients);
}

/*
 ==================
 SV_ReconnectClients
 ==================
*/
static void SV_ReconnectClients (void){

	client_t	*client;
	const char	*reason;
	const char	*packName;
	int			i;

	for (i = 0, client = svs.clients; i < sv_maxClients->integerValue; i++, client++){
		if (client->state < CS_CONNECTED)
			continue;

		// Reconnect the client
		reason = sg->ClientConnect(client->clientNum, false, "noaddr", client->userInfo);
		if (reason){
			// This generally shouldn't happen, because the client was
			// connected before the level change
			SV_DropClient(client, reason);
			continue;
		}

		// Force a new server state to be sent
		client->state = CS_CONNECTED;

		client->serverId = 0;
	}
}

/*
 ==================
 SV_Startup

 Called only if the server wasn't running before
 ==================
*/
static void SV_Startup (void){

	// Update latched variables
	sv_pure = CVar_Get("sv_pure", "1", 0, NULL);
	sv_fps = CVar_Get("sv_fps", "30", 0, NULL);
	sv_maxClients = CVar_Get("sv_maxClients", "32", 0, NULL);

	// Allocate client slots
	CVar_SetInteger(sv_numClients, 0);

	if (sv_maxClients->integerValue < 16)
		CVar_SetInteger(sv_maxClients, 16);
	else if (sv_maxClients->integerValue > MAX_CLIENTS)
		CVar_SetInteger(sv_maxClients, MAX_CLIENTS);

	svs.clients = (client_t *)Mem_ClearedAlloc(sv_maxClients->integerValue * sizeof(client_t), TAG_COMMON);

	// Now active
	svs.initialized = true;

	// Set the server state
	Com_SetServerState(true);

	//start the server
	ENetAddress address;
	address.host = ENET_HOST_ANY;
    address.port = PORT_SERVER;

    svs.server = enet_host_create (& address /* the address to bind the server host to */, 
                                 32      /* allow up to 32 clients and/or outgoing connections */,
                                  0      /* assume any amount of incoming bandwidth */,
                                  0      /* assume any amount of outgoing bandwidth */);
}

/*
 ==================
 SV_Cleanup

 Called only if the server was running before
 ==================
*/
static void SV_Cleanup (void){

	client_t	*client;
	int			i;

	Com_Printf("Changing level...\n");

	// Shutdown game module
	SG_Shutdown();

	// Free the clip map from a previous level
	CM_FreeMap();

	// Update max clients
	SV_UpdateMaxClients();

	// Temporarily stop client downloads because we're going to restart the
	// file system after returning from this function
	for (i = 0, client = svs.clients; i < sv_maxClients->integerValue; i++, client++){
		if (client->state < CS_CONNECTED)
			continue;

	}
	 enet_host_destroy(svs.server);
}

/*
 ==================
 SV_PureRestart
 ==================
*/
static void SV_PureRestart (void){

#ifdef _DEBUG
	CVar_SetInteger(sv_pure, 0);
#endif

	// Restart the file system, setting pure mode if needed
	if (com_dedicated->integerValue){
		FS_Restart(sv_pure->integerValue, NULL);
		return;
	}

	// If not a dedicated server, we also need to restart the client subsystems
	CL_ShutdownAll();

	FS_Restart(sv_pure->integerValue, NULL);

	CL_InitAll();

	// Force a screen update so the connecting GUI is drawn before the server
	// starts loading stuff
	CL_UpdateScreen();
}

/*
 ==================
 SV_SpawnServer

 Change the server to a new level, taking all connected clients along with it
 ==================
*/
void SV_SpawnServer (const char *levelName){

	int		i;

	// Set up some stuff, or clear stuff from a previous level
	if (!svs.initialized)
		SV_Startup();
	else
		SV_Cleanup();

	// If not a dedicated server, connect the local client to the server
	CL_LevelLoading();

	// Set pure mode if needed and restart the file system
	SV_PureRestart();

	// Initialize a new server
	Com_Printf("------- Server Initialization -------\n");
	Com_Printf("Server: %s\n", levelName);

	// Wipe the entire serverActive_t structure
	memset(&sv, 0, sizeof(serverActive_t));

	// Set up the serverActive_t structure
	sv.serverId = com_frameTime;

	if (sv_fps->integerValue < 10)
		CVar_SetInteger(sv_fps, 10);
	else if (sv_fps->integerValue > 125)
		CVar_SetInteger(sv_fps, 125);

	sv.frameMsec = 1000 / sv_fps->integerValue;

	// The server will send this to all clients so they can determine if they
	// need to download something, and to restrict the data sources if pure
	FS_LoadedPackChecksums(sv.packChecksums);

	// Update server info variables
	CVar_SetInteger(sv_serverId, sv.serverId);
	CVar_SetString(sv_map, levelName);

	// Initialize game module
	SG_Init();

	// Tell the game module to load the level and spawn all the entities
	sg->SpawnEntities(levelName, svs.time);

	// Run a few frames to allow everything to settle
	for (i = 0; i < 3; i++){
		svs.time += sv.frameMsec;

		sg->RunFrame(svs.time);
	}

	// Create baselines for more efficient communications
	sg->SetupBaselines();

	// Reconnect all the clients that were connected before
	SV_ReconnectClients();

	// Run another frame to allow things to look at all the players
	svs.time += sv.frameMsec;

	sg->RunFrame(svs.time);

	Com_Printf("-------------------------------------\n");
}
